package com.toolkit.util;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.SM2;
import org.apache.commons.lang3.StringUtils;

import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class ESuperMarketSignUtil {

    // 构造为软引用
    private static final Map<String, SoftReference<SM2>> PUBLIC_KEY_CACHED = new ConcurrentHashMap<String, SoftReference<SM2>>();
    private static final Map<String, SoftReference<SM2>> PRIVATE_KEY_CACHED = new ConcurrentHashMap<String, SoftReference<SM2>>();

    private static final String SEED = "eshop@abc";

    private ESuperMarketSignUtil() {
    }

    /**
     * 加签
     *
     * @return
     */
    public static byte[] sign(String data, String privateKeyStr) {
        // 先进行摘要算法
        String hashcode = SmUtil.sm3(data);
        final SM2 privateKeySM2 = getSm2Instance(privateKeyStr, null);
        return privateKeySM2.sign(hashcode.getBytes());
    }

    private static SM2 getSm2Instance(String privateKeyStr, String publicKeyStr) {

        SM2Cached cached = new SM2Cached(privateKeyStr, publicKeyStr);

        SM2 sm2 = tryGet(cached.waitToPut, cached.getKey());

        if (sm2 != null) {
            return sm2;
        }

        return cached.createAndPut();
    }

    private static SM2 tryGet(Map<String, SoftReference<SM2>> keyCached, String key) {

        SoftReference<SM2> sm2SoftReference = keyCached.get(key);

        if (sm2SoftReference != null) {
            SM2 sm2 = sm2SoftReference.get();
            if (sm2 != null) {
                return sm2;
            }
        }
        return null;
    }

    private static class SM2Cached {

        final boolean putPrivateKey;
        final boolean putPublicKey;

        final String privateKeyStr;
        final String publicKeyStr;

        final Map<String, SoftReference<SM2>> waitToPut;

        public SM2Cached(String privateKeyStr, String publicKeyStr) {

            this.putPrivateKey = StringUtils.isNotBlank(privateKeyStr);
            this.putPublicKey = StringUtils.isNotBlank(publicKeyStr);

            if (putPrivateKey && putPublicKey) {

                this.privateKeyStr = privateKeyStr;
                this.publicKeyStr = publicKeyStr;
                waitToPut = PRIVATE_KEY_CACHED;
            } else if (putPublicKey) {

                this.publicKeyStr = publicKeyStr;
                this.privateKeyStr = null;
                waitToPut = PUBLIC_KEY_CACHED;
            } else if (putPrivateKey) {

                this.publicKeyStr = null;
                this.privateKeyStr = privateKeyStr;
                waitToPut = PRIVATE_KEY_CACHED;
            } else {

                throw new RuntimeException("获取SM2实例失败");
            }
        }

        public String getKey() {

            if (putPrivateKey) {
                return privateKeyStr;
            } else if (putPublicKey) {
                return publicKeyStr;
            } else {
      
                throw new RuntimeException("获取SM2实例失败");
            }
        }

        public SM2 createAndPut() {
            // 无需做同步，并发情况会多创建一份实例，比起同步阻塞的性能消耗无伤大雅
            SM2 sm2 = SmUtil.sm2(privateKeyStr, publicKeyStr);
            if (putPrivateKey && putPublicKey) {
                // 同一对密钥对，保存同一份在公私钥map即可
                PRIVATE_KEY_CACHED.putIfAbsent(privateKeyStr, new SoftReference<SM2>(sm2));
                PUBLIC_KEY_CACHED.putIfAbsent(publicKeyStr, new SoftReference<SM2>(sm2));
            } else if (putPrivateKey) {
                PRIVATE_KEY_CACHED.putIfAbsent(privateKeyStr, new SoftReference<SM2>(sm2));
            } else if (putPublicKey) {
                PUBLIC_KEY_CACHED.putIfAbsent(publicKeyStr, new SoftReference<SM2>(sm2));
            } else {
                throw new RuntimeException("获取SM2实例失败");
            }
            return sm2;
        }
    }

}
